<?php

namespace yunj\app\admin\service\route;

use yunj\app\admin\enum\IsSystem;
use yunj\app\admin\enum\route\RouteDataType;
use yunj\app\admin\enum\State;
use yunj\app\admin\enum\RedisKey;
use yunj\app\admin\enum\RedisKeyGroup;
use yunj\app\admin\enum\EnableLoginAuth;

class RouteRequestService extends Service {

    /**
     * 获取有效（正常）的路由请求项数据（根据base_url获取）
     * @param string $baseUrl
     * @return array
     */
    public static function getValidRouteRequestDatasByBaseUrl(string $baseUrl): array {
        $baseUrl = handle_base_url($baseUrl);
        /** @var RedisKey $keyObj */
        $keyObj = RedisKey::ADMIN_VALID_ROUTE_REQUEST_DATAS_BY_BASE_URL();
        $res = $keyObj->setArgs($baseUrl)->getCacheValue(86400);
        return is_array($res) ? $res : [];
    }

    /**
     * 获取路由请求项数据（含：正常、回收站）
     * @param int|string|mixed $requestId
     * @return array
     */
    public static function getRouteRequestDataById($requestId): array {
        /** @var RedisKey $requestDataObj */
        $keyObj = RedisKey::ADMIN_ROUTE_EQUEST_DATA_BY_ID();
        $data = $keyObj->setArgs($requestId)->getCacheValue(86400, true);
        return is_array($data) ? $data : [];
    }

    /**
     * 获取有效（正常）的路由请求项数据
     * @param int|string|mixed $requestId
     * @return array
     */
    public static function getValidRouteRequestDataById($requestId): array {
        $data = self::getRouteRequestDataById($requestId);
//        if (!$data) {
//            dump($requestId);
//            dd($data);
//        }
        return $data['final_state'] == State::NORMAL ? $data : [];
    }

    /**
     * 获取有效（正常）的路由请求项对应的数据
     * @param string $baseUrl 当前基础地址
     * @param string $method 当前请求method
     * @param array $params 请求参数
     * @return array
     */
    public static function getValidRouteRequestData(string $baseUrl, string $method, array $params): array {
        $data = [];
        $requestDatas = self::getValidRouteRequestDatasByBaseUrl($baseUrl);
        if (!$requestDatas) {
            return $data;
        }
        $currMethod = $method;
        $currAllParams = $params;

        $maxMatch = 0; // 匹配的权限匹配度
        foreach ($requestDatas as $requestData) {
            $match = 0; // 初始匹配指数0
            $res = self::isCurrRequestItemData($match, $requestData, $currMethod, $currAllParams);
            if ($res && $match > $maxMatch) {
                $maxMatch = $match;
                $data = $requestData;
            }
        }
        return $data;
    }

    /**
     * 是否当前请求项数据
     * @param int $match 匹配度(所有条件都满足即为最佳，防止相同method、require_params情况下有多个功能的情况，比如表格构建器的异步操作等)
     * @param array $requestData
     * @param string $currMethod 请求method
     * @param array $currAllParams 所有参数
     * @return bool 是否匹配
     */
    protected static function isCurrRequestItemData(int &$match, array $requestData, string $currMethod, array $currAllParams): bool {
        $matchCalls = self::requestDataMatchCalls();
        // 初始设置不通过
        $res = false;
        foreach ($matchCalls as $call) {
            $res = $call($match, $requestData, $currMethod, $currAllParams);
            if (!$res) {
                // 权限匹配失败
                break;
            }
        }
        return $res;
    }


    /**
     * 请求匹配过滤方法条件数组
     * @return callable[]
     */
    protected static function requestDataMatchCalls(): array {
        return [
            // 校验请求method
            function (int &$match, array $requestData, string $currMethod, array $currAllParams): int {
                $requestMethod = $requestData['method'] ?? '';
                if (!$requestMethod) {
                    // 没有配置method表示都可以接受
                    $match = $match + 1;
                    return true;
                }
                if (strtolower($requestMethod) === strtolower($currMethod)) {
                    // 有配置method，且配置校验通过，则匹配度比没有配置的高1
                    $match = $match + 2;
                    return true;
                }
                return false;
            },
            // 校验请求必须的参数
            function (int &$match, array $requestData, string $currMethod, array $currAllParams): int {
                $requestRequireParams = $requestData['require_params'] ?? [];
                if (!$requestRequireParams) {
                    // 没有配置require_params表示都可以接受
                    $match = $match + 1;
                    return true;
                }

                $_match = 0;
                foreach ($requestRequireParams as $key => $value) {
                    // 匹配参数是否存在
                    if (!array_key_exists($key, $currAllParams)) return false;
                    // 匹配值是否相等
                    if ($value !== null) {
                        if ($value != $currAllParams[$key]) return false;
                    }
                    $_match++;
                }
                if ($_match) {
                    // 有配置require_params，且配置校验通过，则匹配度比没有配置的高1
                    $_match = $_match + 1;
                }
                $match += $_match;

                return true;
            },
        ];
    }

    /**
     * 生成key（含路由、路由分组、路由请求项）
     * @param array $data
     * @param string $type
     * @return string
     */
    public static function key(array $data, string $type = RouteDataType::REQUEST): string {
        switch ($type) {
            case RouteDataType::GROUP:
                // pid、name、desc、namespace、middleware
                $str = $data['pid'] . $data['name'] . $data['desc'] . $data['namespace'] . (is_array($data['middleware']) ? json_encode($data['middleware']) : $data['middleware']);
                break;
            case RouteDataType::ROUTE:
                // group_id、name、rule
                $str = $data['group_id'] . $data['name'] . $data['rule'];
                break;
            case RouteDataType::REQUEST:
                // route_id、method、require_params
                $requireParams = $data['require_params'] && is_string($data['require_params']) ? json_decode($data['require_params'], true) : $data['require_params'];
                $requireParams = $requireParams ?: [];
                ksort($requireParams);
                $str = $data['route_id'] . $data['method'] . json_encode($requireParams);
                break;
        }
        return isset($str) ? md5($str) : '';
    }

    public static function handleItemArgs(array $args, bool $isDb = false): array {
        // method
        if (($method = $args['method'] ?? '')) {
            $method = strtoupper($method);
            if (!in_array($method, ['GET', 'POST', 'DELETE', 'PUT'])) {
                $method = '';
            }
        }
        $args['method'] = $method ?: '';
        // require_params
        $requireParams = $args['require_params'] ?? [];
        if ($requireParams && is_json($requireParams, $requireParamsData, true)) {
            $requireParams = $requireParamsData;
        }
        $requireParams = $requireParams && is_array($requireParams) ? $requireParams : [];
        if ($isDb) {
            $requireParams = $requireParams ? json_encode($requireParams, JSON_UNESCAPED_UNICODE) : '';
        }
        $args['require_params'] = $requireParams;
        // enable_login_auth
        $enableLoginAuth = $args['enable_login_auth'] ?? EnableLoginAuth::ON;
        $args['enable_login_auth'] = EnableLoginAuth::isValue($enableLoginAuth) ? $enableLoginAuth : EnableLoginAuth::ON;

        $args += static::DEF_REQUEST_ITEN_ARGS;
        return $args;
    }

    /**
     * 清理缓存
     */
    public static function clearCache(): void {
        /** @var RedisKeyGroup $keyGroupObj */
        $keyGroupObj = RedisKeyGroup::ADMIN_ROUTE();
        $res = $keyGroupObj->delCacheValue();
    }

}